package com.wnxy.sddfp.trade.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.wnxy.sddfp.client.payback.BorrowerPayPlanApi;
import com.wnxy.sddfp.common.model.dto.payback.AgencyBidPayDTO;
import com.wnxy.sddfp.common.model.dto.payback.BorrowRecordPayDTO;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.wnxy.sddfp.client.bank.TradeLogApi;
import com.wnxy.sddfp.client.system.UserApi;
import com.wnxy.sddfp.common.constant.UserConstant;
import com.wnxy.sddfp.common.enums.AgencyBidStatus;
import com.wnxy.sddfp.common.enums.BorrowRecordStatusDTO;
import com.wnxy.sddfp.common.exception.Assert;
import com.wnxy.sddfp.common.model.dto.bid.AgencyBidDTO;
import com.wnxy.sddfp.common.model.dto.bid.BankBidDTO;
import com.wnxy.sddfp.common.model.dto.bid.BidBalanceDTO;
import com.wnxy.sddfp.common.model.dto.payback.PayBackDTO;
import com.wnxy.sddfp.common.model.vo.bid.AgencyBidVo;
import com.wnxy.sddfp.common.model.vo.bid.LoanDetailVo;
import com.wnxy.sddfp.common.model.vo.bid.LoanVo;
import com.wnxy.sddfp.common.model.vo.user.TokenVo;
import com.wnxy.sddfp.common.model.vo.user.UserInfoVo;
import com.wnxy.sddfp.common.result.Result;
import com.wnxy.sddfp.common.result.enums.impl.BusinessCode;
import com.wnxy.sddfp.common.result.enums.impl.ResultCode;
import com.wnxy.sddfp.common.util.CalculateExpectUtil;
import com.wnxy.sddfp.common.util.IdGenerator;
import com.wnxy.sddfp.common.util.TokenUtils;
import com.wnxy.sddfp.trade.entity.AgencyBid;
import com.wnxy.sddfp.trade.entity.BorrowRecord;
import com.wnxy.sddfp.trade.mapper.AgencyBidMapper;
import com.wnxy.sddfp.trade.mapper.BorrowRecordMapper;
import com.wnxy.sddfp.trade.service.IAgencyBidService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * <p>
 * 机构投标记录 服务实现类
 * </p>
 *
 * @author Jet
 * @since 2023-05-11
 */
@Service
public class AgencyBidServiceImpl extends ServiceImpl<AgencyBidMapper, AgencyBid> implements IAgencyBidService {

    @Autowired
    private UserApi userApi;
    @Autowired
    private TradeLogApi tradeLogApi;
    @Autowired
    private BorrowRecordMapper borrowRecordMapper;
    @Autowired
    private BorrowerPayPlanApi borrowerPayPlanApi;

    @Override
    public AgencyBidVo addAgencyBid(AgencyBidDTO agencyBidDTO) {
        // 从token中获取用户id，根据用户id查询获取用户编码
        TokenVo tokenVo = TokenUtils.getTokenVo();
        Long userId = Long.parseLong(tokenVo.getId());
        Result<UserInfoVo> userInfoVoResult =
                userApi.queryUserById(userId);
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        String userCode = userInfoVo.getUserCode();

        // 调用托管代理，通过托管代理请求托管系统，查询账户余额
        Result<BidBalanceDTO> bidBalance = tradeLogApi.getBidBalance(userCode);
        BidBalanceDTO bidBalanceDTO = bidBalance.getData();

        // 根据账户余额判断，投资金额必须要小于余额
        BigDecimal amount = agencyBidDTO.getAmount();
        if (bidBalanceDTO.getBalance().compareTo(amount) < 0) {
            Assert.error(BusinessCode.BALANCE_NOT_ENOUGH);
        }

        // 重新生成请求流水号
        String reqCode = IdGenerator.generator(UserConstant.REQUEST_CODE_PREFIX);

        // 交易中心保存投资申购记录: 先根据借款id查询，保存到agency_bid表中
        BorrowRecord borrowRecord =
                borrowRecordMapper.selectById(agencyBidDTO.getBorrowRecordId());
        AgencyBid agencyBid = saveAgencyBid(borrowRecord, userInfoVo, userId, amount,reqCode);

        // 调用托管代理，投资申购记录同步到银行托管系统，交易中心---》托管代理---》银行托管
        BankBidDTO bankBidDTO = BankBidDTO.builder()
                .userCode(userCode)
                .reqCode(reqCode)
                .bizType("BID")
                .borrowRecordNo(borrowRecord.getBorrowRecordNo())
                .amount(amount)
                .id(agencyBid.getId())
                .build();
        Result result = tradeLogApi.addBankBidAgency(bankBidDTO);
        if (result.getCode() == ResultCode.SUCCESS.getCode()) {
            // 修改申购记录的状态为1，表示已同步
            agencyBid.setStatus(1);
            this.updateById(agencyBid);
        }

        // 计算预期收益
        // 根据借款期限计算还款月数
        Double ceil =  Math.ceil(borrowRecord.getPeriod() / 30);
        int month = ceil.intValue();
        // 计算预期收益
        BigDecimal expectMoney = CalculateExpectUtil.getIncomeTotalInterest(
                agencyBidDTO.getAmount(),
                borrowRecord.getInvestRate(), month
        );

        // 封装返回结果:AgencyBidVo
        AgencyBidVo agencyBidVo = new AgencyBidVo();
        BeanUtils.copyProperties(agencyBid,agencyBidVo);
        agencyBidVo.setExpectedIncome(expectMoney);
        return agencyBidVo;
    }

    /**
     * 保存机构投资申购记录：agency_bid
     */
    private AgencyBid saveAgencyBid(
            BorrowRecord borrowRecord, UserInfoVo userInfoVo, Long userId, BigDecimal amount,String reqCode) {
        AgencyBid agencyBid = new AgencyBid();
        agencyBid.setUserId(userId);
        agencyBid.setName(userInfoVo.getUserName());
        agencyBid.setUserCode(userInfoVo.getUserCode());
        agencyBid.setAmount(amount);
        agencyBid.setBidStatus("FROZEN");
        agencyBid.setCreateDate(LocalDateTime.now());
        agencyBid.setReqCode(reqCode);
        agencyBid.setStatus(0);

        // 封装借款信息
        agencyBid.setBorrowRecordId(borrowRecord.getId());
        agencyBid.setBorrowRecordNo(borrowRecord.getBorrowRecordNo());
        agencyBid.setBorrowRecordName(borrowRecord.getName());
        agencyBid.setBorrowRecordPeriod(borrowRecord.getPeriod());
        agencyBid.setInvestRate(borrowRecord.getInvestRate());
        // 保存
        this.save(agencyBid);
        return agencyBid;
    }

    @Override
    public String borrowRecordStatus(Long borrowRecordId, String status, String commission) {
        // 第一步：生成放款明细。
        //1.1、根据借款记录ID，查询借款数据； borrow_record表
        BorrowRecord borrowRecord = borrowRecordMapper.selectById(borrowRecordId);
        //1.2、根据借款ID查询机构投资申购记录；agency_bid
        List<AgencyBid> agencyBidList = this.list(
                Wrappers.lambdaQuery(AgencyBid.class)
                        .eq(AgencyBid::getBorrowRecordId, borrowRecordId));

        //1.3、生成放款明细LoanVo ：借款项目id、请求流水号、借款项目编码、平台佣金、【LoanDetailVo 放款金额、preReqCode】
        LoanVo loanVo = createLoadVo(borrowRecord,agencyBidList,commission);

        // 第二步：放款确认：扣除投资人冻结金额并添加到借款人账号中。(代理-->银行)
        Result result = tradeLogApi.confirmLoan(loanVo);
        Assert.error(
                result.getCode() != ResultCode.SUCCESS.getCode(),
                BusinessCode.LEND_MONEY_FAILED
        );

        // 第三步：修改状态。
        // 修改机构投资认购状态为已放款
        agencyBidList.forEach(agencyBid -> {
            agencyBid.setBidStatus(AgencyBidStatus.LOAN.getCode());
            this.updateById(agencyBid);
        });

        // 同步状态，同步到银行托管系统
        BorrowRecordStatusDTO brsDTO=new BorrowRecordStatusDTO();
        brsDTO.setBorrowRecordId(borrowRecord.getId());
        brsDTO.setBorrowStatus(AgencyBidStatus.REPAYING.getCode()); // 状态：还款中
        brsDTO.setReqCode(IdGenerator.generator(UserConstant.REQUEST_CODE_PREFIX));
        brsDTO.setBorrowRecordNo(borrowRecord.getBorrowRecordNo());

        //请求托管代理服务,修改借款项目状态，borrow_record表
        Result<String> borrowRecordResult =
                tradeLogApi.updateBorrowRecordStatus(brsDTO);
        Assert.error(
                borrowRecordResult.getCode() != ResultCode.SUCCESS.getCode(),
                BusinessCode.BANK_STATUS_MODIFY_FAILED
        );
        //处理成功，就修改借款项目(BorrowRecord表)状态为还款中    [接口3完成]
        borrowRecord.setBorrowStatus(AgencyBidStatus.REPAYING.getCode());
        borrowRecordMapper.updateById(borrowRecord);

        // 第四步：请求还款微服务，生成还款计划
        // 交易中心--->还款微服务,生成还款计划
        // 4.1 创建请求还款微服务的dto对象，封装借款、投资机构申购的数据
        PayBackDTO payBackDTO = new PayBackDTO();
        // 4.2 封装借款记录数据dto
        BorrowRecordPayDTO borrowRecordPayDTO = BeanUtil.copyProperties(borrowRecord,BorrowRecordPayDTO.class);
        payBackDTO.setBorrowRecordPayDTO(borrowRecordPayDTO);
        // 4.3 封装机构认购数据: List<AgencyBid> ---> List<AgencyBidPayDTO>
        List<AgencyBidPayDTO> agencyBids = BeanUtil.copyToList(agencyBidList,AgencyBidPayDTO.class);
        payBackDTO.setAgencyBids(agencyBids);
        // 4.4 封装投资人让利、借款人让利
        payBackDTO.setInvestorAnnualRate(new BigDecimal("0.2"));
        payBackDTO.setBorrowerAnnualRate(new BigDecimal("0.3"));

        // TODO 请求还款微服务，生成还款计划
        Result<String> resultPayBack = borrowerPayPlanApi.payPlan(payBackDTO);

        return "OK";
    }

    private LoanVo createLoadVo(BorrowRecord borrowRecord, List<AgencyBid> agencyBidList, String commission) {
        LoanVo loanVo = new LoanVo();
        // 封装借款信息：id
        loanVo.setId(borrowRecord.getId());
        if (StringUtils.hasText(commission)) {
            loanVo.setCommission(new BigDecimal(commission));
        }
        // 封装借款信息：借款编号
        loanVo.setBorrowRecordNo(borrowRecord.getBorrowRecordNo());
        // 请求流水号
        loanVo.setReqCode(IdGenerator.generator(UserConstant.REQUEST_CODE_PREFIX));

        // 封装机构投资认购信息
        List<LoanDetailVo> details = agencyBidList.stream().map(agencyBid -> {
            LoanDetailVo loanDetailVo = new LoanDetailVo();
            loanDetailVo.setAmount(agencyBid.getAmount());
            loanDetailVo.setPreReqCode(agencyBid.getReqCode());
            return loanDetailVo;
        }).collect(Collectors.toList());

        loanVo.setDetails(details);
        return loanVo;
    }
}











